﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Metrics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ESP;

namespace Partition
{
    public class Disk
    {
        public struct PartitionInfo
        {
            /// <summary>
            /// 盘符
            /// </summary>
            public string partitionLetter;

            /// <summary>
            /// 驱动器类型
            /// </summary>
            public System.IO.DriveType partitionType;

            /// <summary>
            /// 驱动器名称
            /// </summary>
            public string partitionName;

            /// <summary>
            /// 驱动器总大小
            /// </summary>
            public long partitionTotalSpace;

            /// <summary>
            /// 驱动器可用空间大小
            /// </summary>
            public long partitionFreeSpace;
        }

        /// <summary>
        /// 获取除ESP分区以外的所有分区
        /// 警告：在执行过程中会卸载ESP分区且不会重新挂载，请在执行MAG_AllESP()前执行本函数
        /// </summary>
        /// <returns></returns>
        public static List<PartitionInfo> GetPartitionInfosExpectESP()
        {
            //卸载所有的ESP分区防止被干扰
            ESP_Partition.UnmountAllESP();

            return GetAllPartition();
        }

        /// <summary>
        /// 挂载并获取所有的ESP分区
        /// </summary>
        /// <returns></returns>
        public static List<PartitionInfo> MAG_AllESP()
        {
            
            //获取挂在ESP分区前的分区列表
            List<PartitionInfo> PIs1 = GetAllPartition();
            //挂载ESP分区
            ESP.ESP_Partition.MountAllESP();
            //获取去挂载后的分区列表
            List<PartitionInfo> PIs2 = GetAllPartition();

            /*
             这时PIs1和PIs2如下（因电脑不同，有所差异）：
            PIs1.partitionLetter[]=['C:\','D:\','E:\','F;\']
            PIs1.partitionLetter[]=['C:\','D:\','E:\','F;\','G:\']
                                                             ▲
                                                           ESP分区
             */

            List<PartitionInfo> espInfo= new List<PartitionInfo>();

            //通过遍历的方法排除常规分区
            foreach (PartitionInfo PI2 in PIs2)
            {
                bool exist = false;

                foreach (PartitionInfo PI1 in PIs1)
                {
                    //如果获取到的两个分区的盘符一致，说明这两个分区是同一个分区，这时将exist设置为true并跳出循环以防止浪费时间
                    if (PI2.partitionLetter == PI1.partitionLetter) {
                        exist = true;
                        break;
                    }
                }

                //跳出循环后，程序通过exist得知便会知道是否是同一个分区，若不是，则证明是ESP分区，将此分区的PartitionInfo添加到espPartition
                if (!exist)
                {
                    espInfo.Add(PI2);
                }
            }

            return espInfo;
        }

        /// <summary>
        /// 对指定盘符所在磁盘的所有esp分区挂载, 已挂载的不会重复挂载, 如果不成功或没有esp分区则返回空列表
        /// </summary>
        /// <param name="Letter">盘符，应为"C"而不是"C:\"</param>
        /// <returns></returns>
        public static List<PartitionInfo> MAG_ESPfromLetter(string Letter)
        {
            List<PartitionInfo> PIs1 = GetAllPartition();

            ESP_Partition.MountESP2Letter(Letter);

            List<PartitionInfo> PIs2 = GetAllPartition();

            List<PartitionInfo> PartitionInfo = new List<PartitionInfo>();

            //通过遍历的方法排除常规分区
            foreach (PartitionInfo PI2 in PIs2)
            {
                bool exist = false;

                foreach (PartitionInfo PI1 in PIs1)
                {
                    //如果获取到的两个分区的盘符一致，说明这两个分区是同一个分区，这时将exist设置为true并跳出循环以防止浪费时间
                    if (PI2.partitionLetter == PI1.partitionLetter)
                    {
                        exist = true;
                        break;
                    }
                }

                //跳出循环后，程序通过exist得知便会知道是否是同一个分区，若不是，则证明是ESP分区，将此分区的PartitionInfo添加到espPartition
                if (!exist)
                {
                    PartitionInfo.Add(PI2);
                }
            }

            return PartitionInfo;
        }

        /// <summary>
        /// 挂载指定硬盘上的所有ESP分区, 已挂载的不会重复挂载, 如果不成功或没有esp分区则返回空列表
        /// </summary>
        /// <param name="DiskNumber" >硬盘号</param>
        /// <returns></returns>
        public static List<PartitionInfo> MAG_ESPfromDisk(int DiskNumber)
        {
            List<PartitionInfo> PIs1 = GetAllPartition();

            ESP_Partition.UnmountAllESPfromDisk(DiskNumber);

            List<PartitionInfo> PIs2 = GetAllPartition();

            List<PartitionInfo> PartitionInfo = new List<PartitionInfo>();

            //通过遍历的方法排除常规分区
            foreach (PartitionInfo PI1 in PIs1)
            {
                bool exist = false;

                foreach (PartitionInfo PI2 in PIs2)
                {
                    //如果获取到的两个分区的盘符一致，说明这两个分区是同一个分区，这时将exist设置为true并跳出循环以防止浪费时间
                    if (PI2.partitionLetter == PI1.partitionLetter)
                    {
                        exist = true;
                        break;
                    }
                }

                //跳出循环后，程序通过exist得知便会知道是否是同一个分区，若不是，则证明是ESP分区，将此分区的PartitionInfo添加到espPartition
                if (!exist)
                {
                    PartitionInfo.Add(PI1);
                }
            }


            return PartitionInfo;
        }

        /// <summary>
        /// 卸载并获取所有卸载的ESP分区, 已卸载的不会重复卸载, 如果不成功或没有esp分区则返回空列表
        /// </summary>
        /// <returns></returns>
        public static List<PartitionInfo> UnMAG_AllESP()
        {

            //获取挂在ESP分区前的分区列表
            List<PartitionInfo> PIs1 = GetAllPartition();
            //挂载ESP分区
            ESP.ESP_Partition.UnmountAllESP(true);
            //获取去挂载后的分区列表
            List<PartitionInfo> PIs2 = GetAllPartition();

            /*
             这时PIs1和PIs2如下（因电脑不同，有所差异）：
            PIs1.partitionLetter[]=['C:\','D:\','E:\','F;\']
            PIs1.partitionLetter[]=['C:\','D:\','E:\','F;\','G:\']
                                                             ▲
                                                           ESP分区
             */

            List<PartitionInfo> espInfo = new List<PartitionInfo>();

            //通过遍历的方法排除常规分区
            foreach (PartitionInfo PI1 in PIs1)
            {
                bool exist = false;

                foreach (PartitionInfo PI2 in PIs2)
                {
                    //如果获取到的两个分区的盘符一致，说明这两个分区是同一个分区，这时将exist设置为true并跳出循环以防止浪费时间
                    if (PI2.partitionLetter == PI1.partitionLetter)
                    {
                        exist = true;
                        break;
                    }
                }

                //跳出循环后，程序通过exist得知便会知道是否是同一个分区，若不是，则证明是ESP分区，将此分区的PartitionInfo添加到espPartition
                if (!exist)
                {
                    espInfo.Add(PI1);
                }
            }

            return espInfo;
        }

        /// <summary>
        /// 对所有盘符所在磁盘的所有ESP分区卸载, 并返回一个列表，已卸载的不会重复卸载, 如果不成功或没有esp分区则返回空列表
        /// </summary>
        /// <param name="Letter">盘符，应为"C"而不是"C:\"</param>
        /// <returns></returns>
        public static List<PartitionInfo> UnMAG_ESPfromLetter(string Letter)
        {
            List<PartitionInfo> PIs1 = GetAllPartition();

            ESP_Partition.UnmountESPfromLetter(Letter,true);

            List<PartitionInfo> PIs2 = GetAllPartition();

            List<PartitionInfo> PartitionInfo = new List<PartitionInfo>();

            //通过遍历的方法排除常规分区
            foreach (PartitionInfo PI1 in PIs1)
            {
                bool exist = false;

                foreach (PartitionInfo PI2 in PIs2)
                {
                    //如果获取到的两个分区的盘符一致，说明这两个分区是同一个分区，这时将exist设置为true并跳出循环以防止浪费时间
                    if (PI2.partitionLetter == PI1.partitionLetter)
                    {
                        exist = true;
                        break;
                    }
                }

                //跳出循环后，程序通过exist得知便会知道是否是同一个分区，若不是，则证明是ESP分区，将此分区的PartitionInfo添加到espPartition
                if (!exist)
                {
                    PartitionInfo.Add(PI1);
                }
            }

            return PartitionInfo;
        }

        /// <summary>
        /// 卸载指定硬盘上的所有ESP分区, 并返回一个列表，已卸载的不会重复卸载, 如果不成功或没有esp分区则返回空列表
        /// </summary>
        /// <param name="DiskNumber" >硬盘号</param>
        /// <returns></returns>
        public static List<PartitionInfo> UnMAG_ESPfromDisk(int DiskNumber)
        {
            List<PartitionInfo> PIs1 = GetAllPartition();

            ESP_Partition.UnmountAllESPfromDisk(DiskNumber, true);

            List<PartitionInfo> PIs2 = GetAllPartition();

            List<PartitionInfo> PartitionInfo = new List<PartitionInfo>();

            //通过遍历的方法排除常规分区
            foreach (PartitionInfo PI1 in PIs1)
            {
                bool exist = false;

                foreach (PartitionInfo PI2 in PIs2)
                {
                    //如果获取到的两个分区的盘符一致，说明这两个分区是同一个分区，这时将exist设置为true并跳出循环以防止浪费时间
                    if (PI2.partitionLetter == PI1.partitionLetter)
                    {
                        exist = true;
                        break;
                    }
                }

                //跳出循环后，程序通过exist得知便会知道是否是同一个分区，若不是，则证明是ESP分区，将此分区的PartitionInfo添加到espPartition
                if (!exist)
                {
                    PartitionInfo.Add(PI1);
                }
            }

            return PartitionInfo;
        }

        /// <summary>
        /// 获取计算机上所有已挂载的盘符
        /// </summary>
        /// <returns></returns>
        public static List<PartitionInfo> GetAllPartition()
        {
            //获取所有磁盘基本信息
            DriveInfo[] DIs=System.IO.DriveInfo.GetDrives();

            //遍历数组
            List<PartitionInfo> PIL=new List<PartitionInfo>();

            //设置信息
            foreach (System.IO.DriveInfo  DI in DIs)
            {
                PartitionInfo info = new PartitionInfo();
                info.partitionLetter = DI.Name;
                info.partitionType = DI.DriveType;
                info.partitionName = DI.VolumeLabel;
                info.partitionTotalSpace = DI.TotalSize;
                info.partitionFreeSpace = DI.TotalFreeSpace;

                PIL.Add(info);

            }

            return PIL;
        }
    }
}
